import logging
from copy import deepcopy
from dataclasses import dataclass
from typing import Dict, List, Sequence

from common.agent_events import ExploitationEvent
from common.agent_plugins import AgentPluginManifest, AgentPluginType
from monkey_island.cc.models import Machine
from monkey_island.cc.repositories import IAgentEventRepository, IMachineRepository
from monkey_island.cc.services.agent_plugin_service import IAgentPluginService

logger = logging.getLogger(__name__)


@dataclass
class MonkeyExploitation:
    label: str
    ip_addresses: List[str]
    domain_name: str
    exploits: List[str]


def get_monkey_exploited(
    event_repository: IAgentEventRepository,
    machine_repository: IMachineRepository,
    agent_plugin_service: IAgentPluginService,
) -> List[MonkeyExploitation]:
    exploits = event_repository.get_events_by_type(ExploitationEvent)
    successful_exploits = [e for e in exploits if e.success]
    plugin_manifests = agent_plugin_service.get_all_plugin_manifests()

    exploited_machines = {
        machine_repository.get_machines_by_ip(e.target)[0] for e in successful_exploits
    }

    exploited = [
        MonkeyExploitation(
            label=str(machine.network_interfaces[0].ip),
            ip_addresses=[str(iface.ip) for iface in machine.network_interfaces],
            domain_name="",
            exploits=get_exploits_used_on_node(machine, successful_exploits, plugin_manifests),
        )
        for machine in exploited_machines
    ]

    return exploited


def get_exploits_used_on_node(
    machine: Machine,
    successful_exploits: Sequence[ExploitationEvent],
    plugin_manifests: Dict[AgentPluginType, Dict[str, AgentPluginManifest]],
) -> List[str]:
    machine_ips = [iface.ip for iface in machine.network_interfaces]
    successful_exploits = [e for e in successful_exploits if e.target in machine_ips and e.success]

    plugin_exploiter_manifests = deepcopy(plugin_manifests.get(AgentPluginType.EXPLOITER, {}))

    exploiter_titles = set()

    for exploit in successful_exploits:
        successful_exploiter_manifest = plugin_exploiter_manifests.get(exploit.exploiter_name)

        if not successful_exploiter_manifest:
            logger.warning(f"Could not find plugin manifest for exploiter {exploit.exploiter_name}")
            continue

        if successful_exploiter_manifest.title:
            exploiter_titles.add(successful_exploiter_manifest.title)
        else:
            exploiter_titles.add(successful_exploiter_manifest.name)

    #  AgentPluginManifest title is Optional[str], list expects Iterable[str]
    return list(exploiter_titles)  # type: ignore[arg-type]
